package flags

import (
	"fmt"
	"reflect"
	"strings"
)

// 设置结构体为启动参数
// 遍历结构体的字段，根据字段名和类型设置命令行参数和环境变量
func SetFlagByStruct(data interface{}) {
	t := reflect.TypeOf(data)    // 获取结构体的类型
	v := reflect.ValueOf(data)   // 获取结构体的值
	farrt := t.Elem()            // 获取结构体的实际类型
	farrv := v.Elem()            // 获取结构体的实际值
	fieldnum := farrt.NumField() // 获取字段数量
	for i := 0; i < fieldnum; i++ {
		fieldt := farrt.Field(i)                  // 获取字段类型
		fieldv := farrv.Field(i)                  // 获取字段值
		lowername := strings.ToLower(fieldt.Name) // 转换字段名为小写
		uppername := strings.ToUpper(fieldt.Name) // 转换字段名为大写
		flag := fieldt.Tag.Get("flag")            // 获取字段的 `flag` 标签
		switch fieldt.Type.Kind() {               // 根据字段类型设置参数
		case reflect.Slice:
			// 如果字段是切片类型，将其值转换为逗号分隔的字符串
			// 并设置为命令行参数
			tmp := fieldv.Interface().([]string)                        // 获取切片值
			SetFlag(lowername, uppername, flag, strings.Join(tmp, ",")) // 设置切片类型参数
		default:
			// 如果字段是其他类型，将其值转换为字符串并设置为命令行参数
			SetFlag(lowername, uppername, flag, fmt.Sprint(fieldv.Interface())) // 设置其他类型参数
		}
	}
}

// 启动参数写进结构体中
// 遍历结构体的字段，根据字段名从命令行参数中获取值并赋值给结构体字段
func LoadStruct(data interface{}) {
	t := reflect.TypeOf(data)    // 获取结构体的类型
	v := reflect.ValueOf(data)   // 获取结构体的值
	farrt := t.Elem()            // 获取结构体的实际类型
	farrv := v.Elem()            // 获取结构体的实际值
	fieldnum := farrt.NumField() // 获取字段数量
	for i := 0; i < fieldnum; i++ {
		fieldt := farrt.Field(i)                  // 获取字段类型
		fieldv := farrv.Field(i)                  // 获取字段值
		lowername := strings.ToLower(fieldt.Name) // 转换字段名为小写
		switch fieldt.Type.Kind() {               // 根据字段类型从命令行参数中获取值
		case reflect.Float64:
			// 如果字段是浮点数类型，从命令行参数中获取值并赋值
			fieldv.SetFloat(GetFlagByFloat64(lowername)) // 设置浮点数值
		case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int8:
			// 如果字段是整数类型，从命令行参数中获取值并赋值
			fieldv.SetInt(int64(GetFlagByInt(lowername))) // 设置整数值
		case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint8:
			// 如果字段是无符号整数类型，从命令行参数中获取值并赋值
			fieldv.SetUint(uint64(GetFlagByUint(lowername))) // 设置无符号整数值
		case reflect.String:
			// 如果字段是字符串类型，从命令行参数中获取值并赋值
			fieldv.SetString(GetFlagByString(lowername)) // 设置字符串值
		case reflect.Slice:
			// 如果字段是切片类型，从命令行参数中获取值并赋值
			fieldv.Set(reflect.ValueOf(GetFlagBySlice(lowername))) // 设置切片值
		case reflect.Bool:
			// 如果字段是布尔类型，从命令行参数中获取值并赋值
			fieldv.Set(reflect.ValueOf(GetFlagByBool(lowername))) // 设置布尔值
		default:
			// 默认处理其他类型
			GetFlagByString(lowername) // 默认处理
		}
	}
}
